//using System;
//using System.Diagnostics;
//using System.IO;
//using System.Text;
//using System.Windows.Forms;
//using System.Xml;
//using JetBrains.ReSharper.UnitTestSupport.PluginInterface;
//using log4net;
//
//namespace JamesKovacs.ReSharper.UnitTestSupport.VstsUnit {
//    [UnitTestAssemblyExplorer("VstsUnit")]
//    public class VstsUnitTestAssembly : UnitTestAssemblyBase {
//        private static readonly ILog s_Logger = LogManager.GetLogger(typeof(VstsUnitTestAssembly));
//
//        private const string resultsFileName = "__VstsUnitTestResultsFile__.trx";
//        private const string xpathTestResult = "/Tests/UnitTestResult[testName/text() = '{0}']";
//        private const string xpathOutcome = "outcome/value__";
//        private const string xpathStdout = "stdout";
//        private const string xpathErrorInfoMessage = "errorInfo/message";
//        private const string xpathErrorInfoStackTrace = "errorInfo/stackTrace";
//        private const string xpathStderr = "stderr";
//        private const string xpathDebugTrace = "debugTrace";
//        private const string xpathDuration = "duration";
//        private string m_UnitTestAssemblyFullPath;
//        private string m_UnitTestAssemblyName;
//        private string m_WorkingDirectory;
//        private string m_ResultsFileFullPath;
//        private string m_RunnerId;
//
//        public VstsUnitTestAssembly(string assemblyToLoad, string remotingUrl, string runnerID) : base(assemblyToLoad, remotingUrl, runnerID) {
//            VstsUnitSetup.Initialize();
//            
//            m_UnitTestAssemblyFullPath = assemblyToLoad;
//            m_UnitTestAssemblyName = Path.GetFileNameWithoutExtension(assemblyToLoad);
//            m_WorkingDirectory = Path.GetDirectoryName(assemblyToLoad);
//            m_ResultsFileFullPath = Path.Combine(m_WorkingDirectory, resultsFileName);
//            m_RunnerId = runnerID;
//        }
//
//        public override void Run(string fixtureName, string[] methodNames, bool[] explicitFlags) {
//            if(string.IsNullOrEmpty(fixtureName)) {
//                throw new ArgumentNullException("fixtureName");
//            }
//            if(methodNames == null || methodNames.Length == 0) {
//                throw new ArgumentNullException("methodNames");
//            }
//            if(explicitFlags == null || methodNames.Length != explicitFlags.Length) {
//                throw new ArgumentException("methodNames[] and explicitFlags[] must be the same length", "explicitFlags");
//            }
//            try {
//                if(logger.IsDebugEnabled) {
//                    StringBuilder sb = new StringBuilder();
//                    sb.AppendFormat("Fixture: {0}{1}", fixtureName, Environment.NewLine);
//                    for(int i = 0; i < methodNames.Length; i++) {
//                        sb.AppendFormat("{0} ({1}){2}", methodNames[i], explicitFlags[i], Environment.NewLine);
//                    }
//                    logger.Debug(sb.ToString());
//                }
//                CleanupRun();
//                RunMSTest(fixtureName, methodNames);
//                ParseAndDisplayResultsFile(fixtureName, methodNames);
//                CleanupRun();
//            } catch(Exception ex) {
//                logger.Error("Error running unit tests", ex);
//                MessageBox.Show(ex.ToString(), "Error Running Unit Tests");
//            }
//        }
//
//        private void CleanupRun() {
//            DirectoryInfo workingDirectory = new DirectoryInfo(m_WorkingDirectory);
//            bool inFound = false;
//            bool outFound = false;
//            foreach(DirectoryInfo dirInfo in workingDirectory.GetDirectories()) {
//                foreach(DirectoryInfo subdirInfo in dirInfo.GetDirectories()) {
//                    switch(subdirInfo.Name) {
//                        case "In":
//                            inFound = true;
//                            break;
//                        case "Out":
//                            outFound = true;
//                            break;
//                    }
//                    if(inFound && outFound) {
//                        dirInfo.Delete(true);
//                    }
//                }
//            }
//
//            if(File.Exists(m_ResultsFileFullPath)) {
//                try {
//                    File.Delete(m_ResultsFileFullPath);
//                } catch(IOException ex) {
//                    throw new IOException(string.Format("Unable to delete temporary results file, {0}, because it is currently in use.", m_ResultsFileFullPath), ex);
//                }
//            }
//        }
//
//        private void RunMSTest(string fixtureName, string[] methodNames) {
//            if(string.IsNullOrEmpty(fixtureName)) {
//                throw new ArgumentNullException("fixtureName");
//            }
//            if(methodNames == null || methodNames.Length == 0) {
//                throw new ArgumentNullException("methodNames");
//            }
//            using(Process msTestProcess = new Process()) {
//                ProcessStartInfo processStartInfo = new ProcessStartInfo(GetMSTestPath(), BuildMSTestParameters(methodNames));
//                processStartInfo.WorkingDirectory = m_WorkingDirectory;
//                processStartInfo.UseShellExecute = false;
//                processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
//                processStartInfo.CreateNoWindow = true;
//                msTestProcess.StartInfo = processStartInfo;
//                msTestProcess.Start();
//                if(methodNames.Length == 1) {
//                    NotifyTestStartup(fixtureName, methodNames[0]);
//                } else {
//                    NotifyFixtureStartup(fixtureName);
//                }
//                msTestProcess.WaitForExit();
//            }
//        }
//
//        private void NotifyFixtureStartup(string fixtureName) {
//            Listener.OnFixtureStartup(m_RunnerId, m_UnitTestAssemblyName, fixtureName);
//        }
//
//        private void NotifyTestStartup(string fixtureName, string methodName) {
//            Listener.OnTestStarted(m_RunnerId, m_UnitTestAssemblyName, fixtureName, methodName);
//        }
//
//        private void ParseAndDisplayResultsFile(string fixtureName, string[] methodNames) {
//            TestResult[] testResults = new TestResult[methodNames.Length];
//            XmlDocument xmlResults = new XmlDocument();
//            // The file might not exist if all tests to be run were marked with Ignore.
//            // If this happens, just follow normal code path with empty XmlDocument.
//            if(File.Exists(m_ResultsFileFullPath)) {
//                xmlResults.Load(m_ResultsFileFullPath);
//            }
//            for(int i = 0; i < methodNames.Length; i++) {
//                string methodName = methodNames[i];
//                NotifyTestStartup(fixtureName, methodName);
//                XmlNode testResultNode = xmlResults.SelectSingleNode(string.Format(xpathTestResult, methodName));
//                TimeSpan duration;
//                // MSTest ignored the test likely due to the Ignore attribute
//                if(testResultNode == null) {
//                    testResults[i] = TestResult.SKIPPED;
//                    duration = new TimeSpan(0);
//                } else {
//                    NotifyConsole(testResultNode.SelectSingleNode(xpathStdout), TestOutputType.STDOUT);
//                    NotifyConsole(testResultNode.SelectSingleNode(xpathErrorInfoMessage), TestOutputType.STDERR);
//                    NotifyConsole(testResultNode.SelectSingleNode(xpathErrorInfoStackTrace), TestOutputType.STDERR);
//                    NotifyConsole(testResultNode.SelectSingleNode(xpathStderr), TestOutputType.STDERR);
//                    NotifyConsole(testResultNode.SelectSingleNode(xpathDebugTrace), TestOutputType.DEBUGTRACE);
//                    duration = TimeSpan.Parse(testResultNode.SelectSingleNode(xpathDuration).InnerXml);
//                    TestOutcome outcome = (TestOutcome)Enum.Parse(typeof(TestOutcome), testResultNode.SelectSingleNode(xpathOutcome).InnerXml);
//                    testResults[i] = ConvertToJetbrainsTestResult(outcome);
//                }
//                NotifyTestFinished(fixtureName, methodName, testResults[i], string.Empty, duration.TotalMilliseconds);
//            }
//            NotifyFixtureTeardown(fixtureName);
//            FixtureTestResult fixtureResult = CombineTestResults(testResults);
//            NotifyFixtureFinished(fixtureName, fixtureResult);
//        }
//
//        private void NotifyConsole(XmlNode xmlNode, TestOutputType testOutputType) {
//            if(xmlNode == null) {
//                return;
//            }
//            string output = xmlNode.InnerXml;
//            if(!string.IsNullOrEmpty(output)) {
//                if(!output.EndsWith(Environment.NewLine)) {
//                    output += Environment.NewLine;
//                }
//                Listener.OnConsole(m_RunnerId, m_UnitTestAssemblyName, output, testOutputType);
//            }
//        }
//
//        private void NotifyTestFinished(string fixtureName, string methodName, TestResult testResult, string message, double time) {
//            Listener.OnTestFinished(m_RunnerId, m_UnitTestAssemblyName, fixtureName, methodName, testResult, message, time);
//        }
//
//        private static FixtureTestResult CombineTestResults(TestResult[] testResults) {
//            bool foundSkippedTest = false;
//            foreach(TestResult testResult in testResults) {
//                switch(testResult) {
//                    case TestResult.ERROR:
//                    case TestResult.FAILED:
//                        // one failed test marks fixture as failed
//                        return FixtureTestResult.FAILED;
//                    case TestResult.SKIPPED:
//                        foundSkippedTest = true;
//                        break;
//                }
//            }
//            return foundSkippedTest ? FixtureTestResult.SKIPPED : FixtureTestResult.SUCCESS;
//        }
//
//        private static TestResult ConvertToJetbrainsTestResult(TestOutcome outcome) {
//            TestResult jetbrainsResult;
//            switch(outcome) {
//                case TestOutcome.Passed:
//                case TestOutcome.PassedButRunAborted:
//                    jetbrainsResult = TestResult.SUCCESS;
//                    break;
//                case TestOutcome.Error:
//                case TestOutcome.Timeout:
//                case TestOutcome.Aborted:
//                    jetbrainsResult = TestResult.ERROR;
//                    break;
//                case TestOutcome.Failed:
//                    jetbrainsResult = TestResult.FAILED;
//                    break;
//                case TestOutcome.NotExecuted:
//                    jetbrainsResult = TestResult.SKIPPED;
//                    break;
//                case TestOutcome.Completed:
//                case TestOutcome.Disconnected:
//                case TestOutcome.InProgress:
//                case TestOutcome.Inconclusive:
//                case TestOutcome.NotRunnable:
//                case TestOutcome.Pending:
//                case TestOutcome.Warning:
//                default:
//                    throw new ApplicationException(string.Format("Unexpected TestOutcome: {0}", outcome));
//            }
//            return jetbrainsResult;
//        }
//
//        private void NotifyFixtureTeardown(string fixtureName) {
//            Listener.OnFixtureTeardown(m_RunnerId, m_UnitTestAssemblyName, fixtureName);
//        }
//
//        private void NotifyFixtureFinished(string fixtureName, FixtureTestResult fixtureResult) {
//            Listener.OnFixtureFinished(m_RunnerId, m_UnitTestAssemblyName, fixtureName, fixtureResult, string.Empty);
//        }
//
//        private static string GetMSTestPath() {
//            string vsTools = Environment.GetEnvironmentVariable("VS80COMNTOOLS");
//            if(string.IsNullOrEmpty(vsTools)) {
//                throw new ApplicationException("Cannot locate MSTest.exe. VS80COMNTOOLS environment variable is not set.");
//            }
//            string msTestPath = Path.GetFullPath(Path.Combine(vsTools, @"..\IDE\MSTest.exe"));
//            if(!File.Exists(msTestPath)) {
//                throw new ApplicationException(string.Format("Cannot locate MSTest.exe. Expected location is {0}", msTestPath));
//            }
//            logger.Debug(msTestPath);
//            return msTestPath;
//        }
//
//        private string BuildMSTestParameters(string[] testNames) {
//            // mstest /nologo /testcontainer:bin\debug\JamesKovacs.ExamplesContractManagerTests.dll /resultsfile:bin\debug\results.trx /test:AddMoney /test:AllocateMoney
//            // N.B. Do not use /unique as mstest.exe will then skip some tests with the same root name. 
//            //      e.g. Two tests: ShouldCalculatePayments and ShouldCalculatePaymentsWithInterest. 
//            //      If /unique is specified, only ShouldCalculatePaymentsWithInterest will be called.
//            // N.B. Do not use /noisolation to speed up tests as configuration files (app.config) will not be loaded.
//            StringBuilder sb = new StringBuilder();
//            sb.AppendFormat("/nologo /testcontainer:{0} /resultsfile:{1}", m_UnitTestAssemblyFullPath, resultsFileName);
//            foreach(string testName in testNames) {
//                sb.AppendFormat(" /test:{0}", testName);
//            }
//            string msTestParameters = sb.ToString();
//            logger.Debug(msTestParameters);
//            return msTestParameters;
//        }
//    }
//}